﻿/*
BSD License

Copyright (C) 2010  Nicolay Mitropolsky <nicolaymitropolsky@gmail.com>

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using foldshot.View;

namespace foldshot
{
    public class DiffTreeConfigurator
    {

        //private DirectoryDiffNode difftree;

        private TreeNode aTreeNode;

        private DiffTreeNodeView diffNodeView;

        public TreeNode ATreeNode
        {
            get { return aTreeNode; }
        }

        private TreeNode bTreeNode;

        public TreeNode BTreeNode
        {
            get { return bTreeNode; }
        }

        private FoldDiff foldDiff = new FoldDiff();
        public Directory ADirectory { get; set; }
        public Directory BDirectory { get; set; }

        public bool DiffOnly
        {
            set
            {
                foldDiff.DiffOnly = value;
            }
            get
            {
                return foldDiff.DiffOnly;
            }
        }

        public string SearchPattern
        {
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    foldDiff.Pattern = null;
                }
                else
                {
                    foldDiff.Pattern = new Wildcard("*"+value+"*",RegexOptions.IgnoreCase);
                }
            }
        }


        public DiffTreeConfigurator(DiffTreeNodeView diffNodeView)
        {
            this.diffNodeView = diffNodeView;
        }

        public DiffTreeConfigurator(Directory a, Directory b, DiffTreeNodeView diffNodeView)
        {
            this.diffNodeView = diffNodeView;
            BuildNodes(a, b);
        }

        public void BuildNodes(Directory a, Directory b)
        {
            this.ADirectory = a;
            this.BDirectory = b;
            BuildNodes();
        }

        public void BuildNodes()
        {
            var difftree = foldDiff.CreateDiffTree(ADirectory, BDirectory);

            buildNodes(difftree);

        }


        private void buildNodes(DirectoryDiffNode difftree)
        {
            if (difftree != null)
            {
                var r = buildDirsRecursive(difftree);
                aTreeNode = r.Item1;
                bTreeNode = r.Item2;
            }
            else
            {
                aTreeNode = null;
                bTreeNode = null;
            }
        }

        private Tuple<DiffTreeNode, DiffTreeNode> buildDirsRecursive(DirectoryDiffNode root)
        {
            var r = buildNodeBase<DirectoryDiffNode, Directory>(root);

            DiffTreeNode a = r.Item1;
            DiffTreeNode b = r.Item2;


            a.LazyLoading = true;
            b.LazyLoading = true;


            Action<DiffTreeNode> lazy = (arg) =>
                                            {

                                                a.Nodes.Clear();
                                                b.Nodes.Clear();

                                                foreach (var dd in root.InnerDirectoryNodes)
                                                {
                                                    var t = buildDirsRecursive(dd);
                                                    a.Nodes.Add(t.Item1);
                                                    b.Nodes.Add(t.Item2);
                                                }


                                                foreach (var dd in root.InnerFileNodes)
                                                {
                                                    var t = buildNodeBase<FileDiffNode, File>(dd);
                                                    a.Nodes.Add(t.Item1);
                                                    b.Nodes.Add(t.Item2);
                                                }

                                                a.LazyLoading = false;
                                                b.LazyLoading = false;
                                            };
            a.DoLazyLoading = lazy;
            b.DoLazyLoading = lazy;

            return Tuple.Create(a, b);


        }



        private Tuple<DiffTreeNode, DiffTreeNode> buildNodeBase<NT, T>(NT root)
            where T : File
            where NT : DiffNode<T>
        {


            Func<DiffTreeNode.DiffState, DiffTreeNode.DiffState, Tuple<DiffTreeNode, DiffTreeNode>> conv =
                (astate, bstate) =>
                {
                    DiffTreeNode a = new DiffTreeNode(root.A, astate, diffNodeView);
                    DiffTreeNode b = new DiffTreeNode(root.B, bstate, diffNodeView);
                    a.BrotherNode = b;
                    b.BrotherNode = a;
                    return Tuple.Create(a, b);
                };

            switch (root.State)
            {
                case DiffState.Same:
                    return conv(DiffTreeNode.DiffState.Common, DiffTreeNode.DiffState.Common);
                    break;
                case DiffState.Aexists:
                    return conv(DiffTreeNode.DiffState.Accent, DiffTreeNode.DiffState.NonExist);
                    break;
                case DiffState.Bexists:
                    return conv(DiffTreeNode.DiffState.NonExist, DiffTreeNode.DiffState.Accent);
                    break;
                case DiffState.Diff:
                    return conv(DiffTreeNode.DiffState.Diff, DiffTreeNode.DiffState.Diff);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("impossible error");

            }
        }

        public void FillTrees(FoldTreeView tw1, FoldTreeView tw2)
        {
            tw1.Nodes.Clear();
            tw2.Nodes.Clear();
            if (aTreeNode != null)
            {
                tw1.Nodes.Add(this.aTreeNode);
            }
            if (bTreeNode != null)
            {
                tw2.Nodes.Add(this.bTreeNode);
            }
        }
    }

    public class DiffTreeNode : TreeNode, ILazyLoadTreeNode
    {
        public enum DiffState { Common, Accent, NonExist, Diff }

        private DiffState state;

        private DiffTreeNodeView view;

        private File file;

        private bool lazyLoading = false;

        public Action<DiffTreeNode> DoLazyLoading;

        public bool LazyLoading
        {
            get { return lazyLoading; }
            set
            {

                if (file.IsDirectory && value)
                {
                    this.Nodes.Add(new TreeNode("dummy"));
                }

                lazyLoading = value;

            }
        }

        private DiffTreeNode brotherNode;

        public DiffTreeNode BrotherNode
        {
            get { return brotherNode; }
            set { brotherNode = value; }
        }

        public DiffState State
        {
            get { return state; }
        }

        public DiffTreeNodeView View
        {
            get { return view; }
            set { view = value; }
        }

        public File File
        {
            get { return file; }
        }

        public DiffTreeNode(File file, DiffState state, DiffTreeNodeView view)
            : base(file.name)
        {
            this.file = file;
            this.state = state;
            this.View = view;

            view.ConfigureGui(this);
        }

        /*
        private void configureGUI()
        {
            switch (DiffState)
            {
                case DiffState.Accent:
                    this.ForeColor = System.Drawing.Color.Green;
                    break;
                case DiffState.NonExist:
                    this.ForeColor = System.Drawing.Color.DimGray;
                    break;
                case DiffState.Diff:
                    this.ForeColor = System.Drawing.Color.OrangeRed;
                    this.Expand();
                    break;
            }

            //this.ToolTipText = this.genShortDescr();

        }*/

        public string genShortDescr()
        {
            return View.GetShortDecription(this);
        }

        public void BeforeExpand()
        {
            if (lazyLoading && file.IsDirectory && this.DoLazyLoading != null)
            {
                this.Nodes.Clear();
                this.DoLazyLoading(this);
            }
        }
    }

}
